(*****************************************************************************
 * Delphi Plug-in Framework (DPF)
 * Copyright (C) 2006-2007 Tao Yuan
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the
 * License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.                                                               }
 *
 * The Initial Developer of the Original Code is Tao Yuan. Portions created by
 * Tao Yuan are Copyright (C) Tao Yuan (taoy.cn att gmail dott com)
 * All rights reserved.
 *****************************************************************************)

unit RDManifestHandler;

interface

uses
  Classes,
  Rcl,
  RDConsts, RDModel, RDBaseHandler;

type

  TBaManifestHandler = class(TBaseHandler)
  private
    manifest          : TModelPluginManifest;
    documentation     : TModelDocumentation;
    prerequisite      : TModelPrerequisite;
    libraryModel      : TModelLibrary;
    extensionPoint    : TModelExtensionPoint;
    extension         : TModelExtension;
    docText           : TStrings;
    attributeStack    : TSimpleStack;
    attribute         : TModelAttribute ;
    paramDefStack     : TSimpleStack ;
    paramDef          : TModelParameterDef ;
    paramStack        : TSimpleStack ;
    param             : TModelParameter ;
    paramValue        : TStrings;  
  protected
    destructor Destroy; override;
  public
    constructor Create(anEntityResolver: IEntityResolver);
    procedure characters(const ch : SAXString); override;
    procedure endElement(const uri, localName, qName: SAXString); override;
    procedure startElement(const uri, localName, qName: SAXString; const atts:
        IAttributes); override;
    function getResult: TModelPluginManifest;
  end;


implementation

uses
  SysUtils;

constructor TBaManifestHandler.Create(anEntityResolver: IEntityResolver);
begin
  inherited Create(anEntityResolver);
end;

destructor TBaManifestHandler.Destroy;
begin
  inherited;
end;

procedure TBaManifestHandler.characters(const ch : SAXString);
begin
  if (docText <> nil) then
      docText.append(ch)
  else if (paramValue <> nil) then
      paramValue.append(ch)
  else if Rcl.Str.StartsWith(ch, #13#10) then
  begin
    //no-op
  end
  else begin
//      if (entityResolver <> nil) then
//        raise ESAXException.Create('unexpected character data'); //$NON-NLS-1$
//
//      // ignore these characters
//      log.warn('ignoring character data - [' //$NON-NLS-1$
//              + ch + ']'); //$NON-NLS-1$
  end;
end;

procedure TBaManifestHandler.endElement(const uri, localName, qName: SAXString);
var
  name: string;
  temp: TObject;
//  tempAttribute: TModelAttribute;
//  tempParameterDef: TModelParameterDef;
begin
  log.debug('endElement - [' + uri + ']/[' + localName //$NON-NLS-1$ //$NON-NLS-2$
          + ']/[' + qName + ']'); //$NON-NLS-1$ //$NON-NLS-2$

  name := qName;
  if Rcl.Str.IsEqual('plugin', name) then //$NON-NLS-1$
      // no-op
  else if Rcl.Str.IsEqual('plugin-fragment', name) then //$NON-NLS-1$
      // no-op
  else if Rcl.Str.IsEqual('doc', name) then //$NON-NLS-1$
  begin
      if (param <> nil) then
          param.documentation := documentation
      else if (extension <> nil) then
          extension.documentation := documentation
      else if (paramDef <> nil) then
          paramDef.documentation := documentation
      else if (extensionPoint <> nil) then
          extensionPoint.documentation := documentation
      else if (libraryModel <> nil) then
          libraryModel.documentation := documentation
      else if (prerequisite <> nil) then
          prerequisite.documentation := documentation
      else if (attribute <> nil) then
          attribute.documentation := documentation
      else
          manifest.documentation := documentation;

      //FreeAndNil(documentation);
      documentation := nil;
  end
  else if Rcl.Str.IsEqual('doc-ref', name) then //$NON-NLS-1$
  begin
      // no-op
  end
  else if Rcl.Str.IsEqual('doc-text', name) then //$NON-NLS-1$
  begin
      documentation.text := docText.Text;
      FreeAndNil(docText);
      //docText := nil;
  end
  else if Rcl.Str.IsEqual('attributes', name) then //$NON-NLS-1$
       FreeAndNil(attributeStack)
//      attributeStack := nil
  else if Rcl.Str.IsEqual('attribute', name) then //$NON-NLS-1$
  begin
      if (attributeStack.size = 0) then
      begin
          manifest.attributes.add(attribute);
          //FreeAndNil(attribute);
          attribute := nil;
      end
      else begin
          temp := attribute;
          attribute := attributeStack.pop as TModelAttribute;
          attribute.attributes.add(temp);
          temp := nil;
      end;
  end else if Rcl.Str.IsEqual('requires', name) then //$NON-NLS-1$
  begin
      // no-op
  end
  else if Rcl.Str.IsEqual('import', name) then //$NON-NLS-1$
  begin
      manifest.prerequisites.Add(prerequisite);
      prerequisite := nil;
  end
  else if Rcl.Str.IsEqual('runtime', name) then //$NON-NLS-1$
  begin
      // no-op
  end
  else if Rcl.Str.IsEqual('library', name) then //$NON-NLS-1$
  begin
      manifest.libraries.Add(libraryModel);
      libraryModel := nil;
  end
  else if Rcl.Str.IsEqual('export', name) then //$NON-NLS-1$
  begin
      // no-op
  end
  else if Rcl.Str.IsEqual('extension-point', name) then //$NON-NLS-1$
  begin
      manifest.extensionPoints.Add(extensionPoint);
      extensionPoint := nil;
      FreeAndNil(paramDefStack);
//      paramDefStack := nil;
  end
  else if Rcl.Str.IsEqual('parameter-def', name) then //$NON-NLS-1$
  begin
      if (paramDefStack.size = 0) then
      begin
          extensionPoint.paramDefs.add(paramDef);
          paramDef := nil;
      end
      else begin
          temp := paramDef;
          paramDef := paramDefStack.pop as TModelParameterDef;
          paramDef.paramDefs.Add(temp);
          temp := nil;
      end;
  end
  else if Rcl.Str.IsEqual('extension', name) then //$NON-NLS-1$
  begin
      manifest.extensions.Add(extension);
      extension := nil;
      FreeAndNil(paramStack);
//      paramStack := nil;
  end
  else if Rcl.Str.IsEqual('parameter', name) then //$NON-NLS-1$
  begin
      if (paramStack.size = 0) then
      begin
          extension.params.Add(param);
          param := nil;
      end
      else begin
          temp := param;
          param := paramStack.pop as TModelParameter;
          param.params.Add(temp);
          temp := nil;
      end;
  end
  else if Rcl.Str.IsEqual('value', name) then //$NON-NLS-1$
  begin
      param.value := paramValue.Text;
      FreeAndNil(paramValue);
//      paramValue := nil;
  end
  else begin
      // ignore any other element
      log.warn('ignoring manifest element - [' + uri + ']/[' //$NON-NLS-1$
              + localName + ']/[' + qName + ']'); //$NON-NLS-1$ //$NON-NLS-2$
  end;
end;

function TBaManifestHandler.getResult: TModelPluginManifest;
begin
  Result := manifest;
end;

procedure TBaManifestHandler.startElement(const uri, localName, qName: SAXString;
    const atts: IAttributes);
var
  name: string;
  docRef : TModelDocumentationReference;
begin
  log.debug('startElement - [' + uri + ']/[' //$NON-NLS-1$ //$NON-NLS-2$
          + localName + ']/[' + qName + ']'); //$NON-NLS-1$ //$NON-NLS-2$

  name := qName;
  if Rcl.Str.IsEqual('plugin', name) then //$NON-NLS-1$
  begin
      if (manifest <> nil) then
          raise ESAXException.Create('unexpected [' + name //$NON-NLS-1$
                  + '] element (manifest already defined)'); //$NON-NLS-1$

      manifest := TModelPluginDescriptor.Create;
      manifest.id := atts.getValue('id'); //$NON-NLS-1$
      manifest.Setversion(atts.getValue('version')); //$NON-NLS-1$
      manifest.vendor := atts.getValue('vendor'); //$NON-NLS-1$
      manifest.docsPath := atts.getValue('docs-path'); //$NON-NLS-1$
      //to remove. Needn't in delphi
      TModelPluginDescriptor(manifest).PluginClassName :=
              atts.getValue('class'); //$NON-NLS-1$
  end
  else if Rcl.Str.IsEqual('plugin-fragment', name) then //$NON-NLS-1$
  begin
      if (manifest <> nil) then
          raise ESAXException.Create('unexpected [' + name //$NON-NLS-1$
                  + '] element (manifest already defined)'); //$NON-NLS-1$

      manifest := TModelPluginFragment.Create;
      manifest.Id := (atts.getValue('id')); //$NON-NLS-1$
      manifest.setVersion(atts.getValue('version')); //$NON-NLS-1$
      manifest.Vendor := (atts.getValue('vendor')); //$NON-NLS-1$
      manifest.docsPath := (atts.getValue('docs-path')); //$NON-NLS-1$
      TModelPluginFragment(manifest).PluginId := (
              atts.getValue('plugin-id')); //$NON-NLS-1$
      if (atts.getValue('plugin-version') <> '') then //$NON-NLS-1$
          TModelPluginFragment(manifest).SetpluginVersion(
                  atts.getValue('plugin-version')); //$NON-NLS-1$

      if (atts.getValue('match') <> '') then //$NON-NLS-1$
          TModelPluginFragment(manifest).match := (
                  atts.getValue('match')) //$NON-NLS-1$
      else
          TModelPluginFragment(manifest).match := (MATCH_COMPATIBLE);

  end
  else if Rcl.Str.IsEqual('doc', name) then //$NON-NLS-1$
  begin
      documentation := TModelDocumentation.Create;
      documentation.caption := (atts.getValue('caption')); //$NON-NLS-1$
  end
  else if Rcl.Str.IsEqual('doc-ref', name) then //$NON-NLS-1$
  begin
      if (documentation = nil) then
      begin
          if (entityResolver <> nil) then
              raise ESAXException.Create('[doc-ref] element found ' //$NON-NLS-1$
                      + 'outside of [doc] element'); //$NON-NLS-1$

          // ignore this element
          log.warn('[doc-ref] element found outside of [doc] element'); //$NON-NLS-1$
          Exit;
      end;

      docRef := TModelDocumentationReference.Create;
      docRef.path := (atts.getValue('path')); //$NON-NLS-1$
      docRef.caption := (atts.getValue('caption')); //$NON-NLS-1$
      documentation.references.Add(docRef);
  end
  else
  if Rcl.Str.IsEqual('doc-text', name) then //$NON-NLS-1$
  begin
      if (documentation = nil) then
      begin
          if (entityResolver <> nil) then
              raise ESAXException.Create('[doc-text] element found ' //$NON-NLS-1$
                      + 'outside of [doc] element'); //$NON-NLS-1$

          // ignore this element
          log.warn('[doc-text] element found outside of [doc] element'); //$NON-NLS-1$
          Exit;
      end;
      docText := TStringList.Create;
  end
  else if Rcl.Str.IsEqual('atts', name) then //$NON-NLS-1$
  begin
      attributeStack := TSimpleStack.Create;
  end
  else if Rcl.Str.IsEqual('attribute', name) then  //$NON-NLS-1$
  begin
      if (attributeStack = nil) then
      begin
          if (entityResolver <> nil) then
              raise ESAXException.Create('[attribute] element found ' //$NON-NLS-1$
                      + 'outside of [atts] element'); //$NON-NLS-1$

          // ignore this element
          log.warn('[attribute] element found ' //$NON-NLS-1$
                  + 'outside of [atts] element'); //$NON-NLS-1$
          Exit;
      end;
      if (attribute <> nil) then
          attributeStack.push(attribute);
            
      attribute := TModelAttribute.Create;
      attribute.id := (atts.getValue('id')); //$NON-NLS-1$
      attribute.value := (atts.getValue('value')); //$NON-NLS-1$
  end
  else if Rcl.Str.IsEqual('requires', name) then
  begin //$NON-NLS-1$
      // no-op
  end
  else if Rcl.Str.IsEqual('import', name) then //$NON-NLS-1$
  begin
      prerequisite := TModelPrerequisite.Create;
      if (atts.getValue('id') <> '') then //$NON-NLS-1$
          prerequisite.id := (atts.getValue('id')); //$NON-NLS-1$

      prerequisite.pluginId := (atts.getValue('plugin-id')); //$NON-NLS-1$
      if (atts.getValue('plugin-version') <> '') then //$NON-NLS-1$
          prerequisite.setPluginVersion(
                  atts.getValue('plugin-version')); //$NON-NLS-1$

      if (atts.getValue('match') <> '') then //$NON-NLS-1$
          prerequisite.match := (atts.getValue('match')) //$NON-NLS-1$
      else
          prerequisite.match := (MATCH_COMPATIBLE);

      prerequisite.isExported := Rcl.Str.ToBool(atts.getValue('exported')); //$NON-NLS-1$
      prerequisite.isOptional := Rcl.Str.ToBool(atts.getValue('optional')); //$NON-NLS-1$
  end
  else if Rcl.Str.IsEqual('runtime', name) then //$NON-NLS-1$
  begin
      // no-op
  end
  else if Rcl.Str.IsEqual('library', name) then //$NON-NLS-1$
  begin
      libraryModel := TModelLibrary.Create;
      libraryModel.id := (atts.getValue('id')); //$NON-NLS-1$
      libraryModel.path := (atts.getValue('path')); //$NON-NLS-1$
      libraryModel.setCodeLibrary(atts.getValue('type')); //$NON-NLS-1$
      if (atts.getValue('version') <> '') then //$NON-NLS-1$
          libraryModel.setVersion(atts.getValue('version')); //$NON-NLS-1$

  end
  else if Rcl.Str.IsEqual('export', name) then //$NON-NLS-1$
  begin
      if (libraryModel = nil) then
      begin
          if (entityResolver <> nil) then
             raise ESAXException.Create('[export] element found ' //$NON-NLS-1$
                      + 'outside of [library] element'); //$NON-NLS-1$

          // ignore this element
          log.warn('[export] element found outside of [library] element'); //$NON-NLS-1$
          Exit;
      end;
      libraryModel._exports.Add(atts.getValue('prefix')); //$NON-NLS-1$
  end
  else if Rcl.Str.IsEqual('extension-point', name) then //$NON-NLS-1$
  begin
      extensionPoint := TModelExtensionPoint.Create;
      extensionPoint.id := (atts.getValue('id')); //$NON-NLS-1$
      extensionPoint.parentPluginId := (
              atts.getValue('parent-plugin-id')); //$NON-NLS-1$
      extensionPoint.parentPointId := (
              atts.getValue('parent-point-id')); //$NON-NLS-1$
      if (atts.getValue('extension-multiplicity') <> '') then //$NON-NLS-1$
          extensionPoint.extensionMultiplicity := (
                  atts.getValue('extension-multiplicity')) //$NON-NLS-1$
      else
          extensionPoint.extensionMultiplicity := EXT_MULT_ANY;

      paramDefStack := TSimpleStack.Create;
  end
  else if Rcl.Str.IsEqual('parameter-def', name) then  //$NON-NLS-1$
  begin
      if (extensionPoint = nil) then
      begin
          if (entityResolver <> nil) then
              raise ESAXException.Create('[parameter-def] element found ' //$NON-NLS-1$
                      + 'outside of [extension-point] element'); //$NON-NLS-1$

          // ignore this element
          log.warn('[parameter-def] element found ' //$NON-NLS-1$
                  + 'outside of [extension-point] element'); //$NON-NLS-1$
          Exit;
      end;
      if (paramDef <> nil) then
          paramDefStack.push(paramDef);

      paramDef := TModelParameterDef.Create;
      paramDef.id := (atts.getValue('id')); //$NON-NLS-1$
      if (atts.getValue('multiplicity') <> '') then //$NON-NLS-1$
          paramDef.multiplicity := (atts.getValue('multiplicity')) //$NON-NLS-1$
      else
          paramDef.multiplicity := (MULT_ONE);

      if (atts.getValue('type') <> '') then  //$NON-NLS-1$
          paramDef._type := (atts.getValue('type')) //$NON-NLS-1$
      else
          paramDef._type := (TYPE_STRING);

      paramDef.customData := (atts.getValue('custom-data')); //$NON-NLS-1$
  end
  else if Rcl.Str.IsEqual('extension', name) then //$NON-NLS-1$
  begin
      extension := TModelExtension.Create;
      extension.id := (atts.getValue('id')); //$NON-NLS-1$
      extension.pluginId := (atts.getValue('plugin-id')); //$NON-NLS-1$
      extension.pointId := (atts.getValue('point-id')); //$NON-NLS-1$
      paramStack := TSimpleStack.Create;
  end
  else if Rcl.Str.IsEqual('parameter', name) then //$NON-NLS-1$
  begin
      if (extension = nil) then
      begin
          if (entityResolver <> nil) then
              raise ESAXException.Create('[parameter] element found ' //$NON-NLS-1$
                      + 'outside of [extension] element'); //$NON-NLS-1$

          // ignore this element
          log.warn('[parameter] element found ' //$NON-NLS-1$
                  + 'outside of [extension] element'); //$NON-NLS-1$
          Exit;
      end;
      if (param <> nil) then
          paramStack.push(param);

      param := TModelParameter.Create;
      param.id := (atts.getValue('id')); //$NON-NLS-1$
      param.value := (atts.getValue('value')); //$NON-NLS-1$
  end
  else if Rcl.Str.IsEqual('value', name) then //$NON-NLS-1$
  begin
      if (param = nil) then
      begin
          if (entityResolver <> nil) then
              raise ESAXException.Create('[value] element found ' //$NON-NLS-1$
                      + 'outside of [parameter] element'); //$NON-NLS-1$

          // ignore this element
          log.warn('[value] element found ' //$NON-NLS-1$
                  + 'outside of [parameter] element'); //$NON-NLS-1$
          Exit;
      end;
      paramValue := TStringList.Create;
  end
  else
  begin
      if (entityResolver <> nil) then
          raise ESAXException.Create('unexpected manifest element - [' //$NON-NLS-1$
                  + uri + ']/[' + localName + ']/[' + qName + ']'); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

      // ignore this element
      log.warn('unexpected manifest element - [' + uri + ']/[' //$NON-NLS-1$ //$NON-NLS-2$
              + localName + ']/[' + qName + ']'); //$NON-NLS-1$ //$NON-NLS-2$
  end;
end;



end.
